/*******************************************************************************
* Copyright (c) 2016 Weasis Team and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Nicolas Roduit - initial API and implementation
*******************************************************************************/
package org.weasis.core.api.media.data;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Component;
import java.awt.Composite;
import java.awt.Container;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DragSourceMotionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.weasis.core.api.gui.util.AppProperties;
import org.weasis.core.api.gui.util.GhostGlassPane;
import org.weasis.core.api.image.OpManager;
import org.weasis.core.api.media.data.MediaSeries.MEDIA_POSITION;
import org.weasis.core.api.util.FileUtil;
import org.weasis.core.api.util.FontTools;
public class SeriesThumbnail extends Thumbnail
implements MouseListener, DragGestureListener, DragSourceListener, DragSourceMotionListener, FocusListener {
private static final long serialVersionUID = 2359304176364341395L;
private static final Logger LOGGER = LoggerFactory.getLogger(SeriesThumbnail.class);
private static final int BUTTON_SIZE_HALF = 7;
private static final Polygon startButton = new Polygon(new int[] { 0, 2 * BUTTON_SIZE_HALF, 0 },
new int[] { 0, BUTTON_SIZE_HALF, 2 * BUTTON_SIZE_HALF }, 3);
private static final Rectangle stopButton = new Rectangle(0, 0, 2 * BUTTON_SIZE_HALF, 2 * BUTTON_SIZE_HALF);
private static final Composite SOLID_COMPOSITE = AlphaComposite.SrcOver;
private static final Composite TRANSPARENT_COMPOSITE = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.2f);
private MediaSeries.MEDIA_POSITION mediaPosition = MediaSeries.MEDIA_POSITION.MIDDLE;
// Get the closest cursor size regarding to the platform
private final Border onMouseOverBorderFocused =
new CompoundBorder(new EmptyBorder(2, 2, 0, 2), new LineBorder(Color.orange, 2));
private final Border onMouseOverBorder =
new CompoundBorder(new EmptyBorder(2, 2, 0, 2), new LineBorder(new Color(255, 224, 178), 2));
private final Border outMouseOverBorder =
new CompoundBorder(new EmptyBorder(2, 2, 0, 2), BorderFactory.createEtchedBorder());
private JProgressBar progressBar;
private final MediaSeries<? extends MediaElement> series;
private Point dragPressed = null;
private DragSource dragSource = null;
public SeriesThumbnail(final MediaSeries<? extends MediaElement> sequence, int thumbnailSize) {
super(thumbnailSize);
if (sequence == null) {
throw new IllegalArgumentException("Sequence cannot be null"); //$NON-NLS-1$
}
this.series = sequence;
// media can be null for seriesThumbnail
MediaElement media = sequence.getMedia(MEDIA_POSITION.MIDDLE, null, null);
// Handle special case for DICOM SR
if (media == null) {
List<MediaElement> specialElements = (List<MediaElement>) series.getTagValue(TagW.DicomSpecialElementList);
if (specialElements != null && !specialElements.isEmpty()) {
media = specialElements.get(0);
}
}
/*
* Do not remove the image from the cache after building the thumbnail when the series is associated to a
* explorerModel (stream should be closed at least when closing the application or when free the cache).
*/
init(media, series.getTagValue(TagW.ExplorerModel) != null, null);
}
@Override
protected void init(MediaElement media, boolean keepMediaCache, OpManager opManager) {
super.init(media, keepMediaCache, opManager);
setBorder(outMouseOverBorder);
}
public JProgressBar getProgressBar() {
return progressBar;
}
public void setProgressBar(JProgressBar progressBar) {
if (progressBar == null) {
removeMouseListener(this);
} else {
if (!Arrays.asList(this.getMouseListeners()).contains(this)) {
addMouseListener(this);
}
}
this.progressBar = progressBar;
}
@Override
public void registerListeners() {
super.registerListeners();
// Reactivate tooltip listener
ToolTipManager.sharedInstance().registerComponent(this);
if (dragSource != null) {
dragSource.removeDragSourceListener(this);
dragSource.removeDragSourceMotionListener(this);
removeFocusListener(this);
}
addFocusListener(this);
this.setFocusable(true);
this.addMouseListener(this);
dragSource = new DragSource();
dragSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, this);
dragSource.addDragSourceMotionListener(this);
}
public void reBuildThumbnail(MediaSeries.MEDIA_POSITION position) {
reBuildThumbnail(null, position);
}
public void reBuildThumbnail() {
reBuildThumbnail(null, mediaPosition);
}
public synchronized void reBuildThumbnail(File file, MediaSeries.MEDIA_POSITION position) {
MediaElement media = series.getMedia(position, null, null);
// Handle special case for DICOM SR
if (media == null) {
List<MediaElement> specialElements = (List<MediaElement>) series.getTagValue(TagW.DicomSpecialElementList);
if (specialElements != null && !specialElements.isEmpty()) {
media = specialElements.get(0);
}
}
if (file != null || media != null) {
mediaPosition = position;
if (thumbnailPath != null && thumbnailPath.getPath().startsWith(AppProperties.FILE_CACHE_DIR.getPath())) {
FileUtil.delete(thumbnailPath); // delete old temp file
}
thumbnailPath = file;
readable = true;
/*
* Do not remove the image from the cache after building the thumbnail when the series is associated to a
* explorerModel (stream should be closed at least when closing the application or when free the cache).
*/
buildThumbnail(media, series.getTagValue(TagW.ExplorerModel) != null, null);
revalidate();
repaint();
}
}
public synchronized int getThumbnailSize() {
return thumbnailSize;
}
public synchronized void setThumbnailSize(int thumbnailSize) {
boolean update = this.thumbnailSize != thumbnailSize;
if (update) {
Object media = series.getMedia(mediaPosition, null, null);
this.thumbnailSize = thumbnailSize;
buildThumbnail((MediaElement) media, series.getTagValue(TagW.ExplorerModel) != null, null);
}
}
// --- DragGestureListener methods -----------------------------------
@Override
public void dragGestureRecognized(DragGestureEvent dge) {
Component comp = dge.getComponent();
try {
GhostGlassPane glassPane = AppProperties.glassPane;
glassPane.setIcon(getIcon());
Point p = (Point) dge.getDragOrigin().clone();
dragPressed = new Point(p.x - 4, p.y - 4);
SwingUtilities.convertPointToScreen(p, comp);
drawGlassPane(p);
glassPane.setVisible(true);
dge.startDrag(null, series, this);
} catch (Exception e) {
LOGGER.error("Prepare to drag", e); //$NON-NLS-1$
}
}
@Override
public void dragMouseMoved(DragSourceDragEvent dsde) {
drawGlassPane(dsde.getLocation());
}
// --- DragSourceListener methods -----------------------------------
@Override
public void dragEnter(DragSourceDragEvent dsde) {
// Do nothing
}
@Override
public void dragOver(DragSourceDragEvent dsde) {
// Do nothing
}
@Override
public void dragExit(DragSourceEvent dsde) {
// Do nothing
}
@Override
public void dragDropEnd(DragSourceDropEvent dsde) {
GhostGlassPane glassPane = AppProperties.glassPane;
dragPressed = null;
glassPane.setImagePosition(null);
glassPane.setIcon(null);
glassPane.setVisible(false);
}
@Override
public void dropActionChanged(DragSourceDragEvent dsde) {
// Do nothing
}
public void drawGlassPane(Point p) {
if (dragPressed != null) {
GhostGlassPane glassPane = AppProperties.glassPane;
SwingUtilities.convertPointFromScreen(p, glassPane);
p.translate(-dragPressed.x, -dragPressed.y);
glassPane.setImagePosition(p);
}
}
public MediaSeries<MediaElement> getSeries() {
return (MediaSeries<MediaElement>) series;
}
@Override
public void focusGained(FocusEvent e) {
if (!e.isTemporary()) {
JPanel container = getScrollPane();
if (container != null) {
Rectangle bound = this.getBounds();
Point p1 = SwingUtilities.convertPoint(this, this.getX(), this.getY(), container);
bound.x = p1.x;
bound.y = p1.y;
container.scrollRectToVisible(bound);
}
SeriesImporter loader = series.getSeriesLoader();
if (loader != null) {
loader.setPriority();
}
}
}
@Override
public void focusLost(FocusEvent e) {
// Do nothing
}
private JPanel getScrollPane() {
Container container = getParent();
while (container != null) {
if (container.getParent() instanceof JViewport) {
return (JPanel) container;
}
container = container.getParent();
}
return null;
}
@Override
public String getToolTipText() {
return series.getToolTips();
}
@Override
protected void drawOverIcon(Graphics2D g2d, int x, int y, int width, int height) {
if (dragPressed == null) {
setBorder(series.isSelected() ? series.isFocused() ? onMouseOverBorderFocused : onMouseOverBorder
: outMouseOverBorder);
if (series.isOpen()) {
g2d.setPaint(Color.BLACK);
g2d.fillRect(x, y, 11, 11);
g2d.setPaint(Color.GREEN);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.fillArc(x + 2, y + 2, 7, 7, 0, 360);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_DEFAULT);
}
g2d.setFont(FontTools.getFont10());
int fontHeight = (int) (FontTools.getAccurateFontHeight(g2d) + 1.5f);
Integer splitNb = (Integer) series.getTagValue(TagW.SplitSeriesNumber);
if (splitNb != null) {
String nb = "#" + splitNb; //$NON-NLS-1$
int w = g2d.getFontMetrics().stringWidth(nb);
g2d.setPaint(Color.BLACK);
int sx = x + width - 2 - w;
g2d.fillRect(sx - 2, y, w + 4, fontHeight);
g2d.setPaint(Color.ORANGE);
g2d.drawString(nb, sx, y + fontHeight - 3);
}
String nbImg = "[" + series.size(null) + "]"; //$NON-NLS-1$ //$NON-NLS-2$
int hbleft = y + height - 3;
int w = g2d.getFontMetrics().stringWidth(nbImg);
g2d.setPaint(Color.BLACK);
g2d.fillRect(x, y + height - fontHeight, w + 4, fontHeight);
g2d.setPaint(Color.ORANGE);
g2d.drawString(nbImg, x + 2, hbleft);
// To avoid concurrency issue
final JProgressBar bar = progressBar;
if (bar != null) {
if (series.getFileSize() > 0.0) {
g2d.drawString(FileUtil.formatSize(series.getFileSize()), x + 2, hbleft - 12);
}
if (bar.isVisible()) {
// Draw in the bottom right corner of thumbnail
int shiftx = thumbnailSize - bar.getWidth();
int shifty = thumbnailSize - bar.getHeight();
g2d.translate(shiftx, shifty);
bar.paint(g2d);
// Draw in the top right corner
SeriesImporter seriesLoader = series.getSeriesLoader();
if (seriesLoader != null) {
boolean stopped = seriesLoader.isStopped();
g2d.translate(-shiftx, -shifty);
shiftx = thumbnailSize - stopButton.width;
shifty = 5;
g2d.translate(shiftx, shifty);
g2d.setColor(Color.RED);
g2d.setComposite(stopped ? TRANSPARENT_COMPOSITE : SOLID_COMPOSITE);
g2d.fill(stopButton);
g2d.translate(-shiftx, -shifty);
shiftx = shiftx - 3 * BUTTON_SIZE_HALF;
shifty = 5;
g2d.translate(shiftx, shifty);
g2d.setColor(Color.GREEN);
g2d.setComposite(stopped ? SOLID_COMPOSITE : TRANSPARENT_COMPOSITE);
g2d.fill(startButton);
g2d.translate(-shiftx, -shifty);
}
}
}
}
}
@Override
public void mouseClicked(MouseEvent e) {
// Do nothing
}
@Override
public void mousePressed(MouseEvent e) {
// Do nothing
}
@Override
public void mouseReleased(MouseEvent e) {
if (progressBar != null) {
// To avoid concurrency issue
JProgressBar bar = progressBar;
if (bar.isVisible()) {
Point p = e.getPoint();
p.translate(-(thumbnailSize - stopButton.width), -5);
if (stopButton.contains(p)) {
SeriesImporter loader = series.getSeriesLoader();
if (loader != null) {
loader.stop();
}
repaint();
return;
}
p.translate(3 * BUTTON_SIZE_HALF, 0);
if (startButton.getBounds().contains(p)) {
SeriesImporter loader = series.getSeriesLoader();
if (loader != null) {
loader.resume();
}
repaint();
}
}
}
}
@Override
public void mouseEntered(MouseEvent e) {
// Do nothing
}
@Override
public void mouseExited(MouseEvent e) {
// Do nothing
}
}